package com.example.lcpractice.thread;

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;

class MyDate {
    //共享变量 1.1 不加volatile关键字没有可见性，加了后主线程可见性
      int number = 0;
    //2.3 原子性int
    AtomicInteger atomicInteger = new AtomicInteger();
    // 原子性方法
    public void addWithAtomic(){
        atomicInteger.getAndIncrement();
    }


    public void change() {
        this.number = 60;
    }

    public void add() {
        number++;
    }
}

/**
 * 1.验证volatile的可见性
 */
public class VolatileDemo {
    public static void main(String[] args) {
        func3();
    }



    /**
     * 1.校验valitile可见性
     */
    public static void func11() {
        System.out.println(Thread.currentThread().getName());
        MyDate myDate = new MyDate();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDate.change();
            System.out.println(Thread.currentThread().getName() + "\t update number value=" + myDate.number);
        }, "A线程").start();

        //main线程读到的数据
        while (myDate.number == 0) {
            //main 线程一直循环等待直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t over number value=" + myDate.number);
    }

    /**
     * 1.校验valitile可见性
     */
    public static void func1() {
        System.out.println(Thread.currentThread().getName());
        MyDate myDate = new MyDate();
        new Thread(() -> {
            System.out.println(Thread.currentThread().getName() + "\t come in");
            try {
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            myDate.change();
            System.out.println(Thread.currentThread().getName() + "\t update number value=" + myDate.number);
        }, "A线程").start();

        //main线程读到的数据
        while (myDate.number == 0) {
            //main 线程一直循环等待直到number值不等于0
        }
        System.out.println(Thread.currentThread().getName() + "\t over number value=" + myDate.number);
    }

    /**
     * 2.不保证原子性
     * n++这个操作在字节码中分为了三个步骤
     *
     * getfield ---- 拿到原始的值n
     * iadd -------- 进行+1操作
     * putfield -----把累加后的值写回
     *
     * 当在多线程环境中，如果多个线程都拿到了原始的n，运算完成后在写回数值之前线程被挂起或阻塞，线程恢复后来不及收到可见性通知就向主内存写下了数值，就会发生写覆盖的情况，丢失数据。
     *
     * 导致了在本程序中无法达到10000.
     */
    public static void func2() {
        MyDate myDate = new MyDate();
        // 生成10个线程，每个线程1000次加，理论结果是10000
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myDate.add();
                }
            }, String.valueOf(i)).start();
        }
        // 等待线程计算完成后 由main线程取最终结果 10*1000 = 1万
        while (Thread.activeCount() > 2) { //mian线程和GC后台线程
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t sum value=" + myDate.number);
    }

    /**
     *  3 . 原子类操作
     */
    public static void func3( ){
        MyDate myDate = new MyDate();
        // 生成10个线程，每个线程1000次加，理论结果是10000
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    myDate.addWithAtomic();
                }
            }, String.valueOf(i)).start();
        }
        // 等待线程计算完成后 由main线程取最终结果 10*1000 = 1万
        while (Thread.activeCount() > 2) { //mian线程和GC后台线程
            Thread.yield();
        }
        System.out.println(Thread.currentThread().getName() + "\t sum atomicValue=" + myDate.atomicInteger);

    }
}

